home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 076-100 / 092 / bawk / bawk.c < prev    next >
C/C++ Source or Header  |  1995-03-13  |  13KB  |  678 lines

  1. /*
  2.  * Bawk main program
  3.  */
  4. #define MAIN 1
  5. #include <stdio.h>
  6. #include "bawk.h"
  7.  
  8. static char *pattern_arg = NULL; /* Command line bawk program pattern */
  9. static int ungetc_arg = 0;
  10. static char eof_seen = 0;
  11. static int max_field_count = 0;
  12.                 
  13. /*
  14.  * Main program
  15.  */
  16. main( argc, argv )
  17. register int argc;
  18. register char **argv;
  19. {
  20.     register char gotrules = 0, didfile = 0, getstdin = 0;
  21.     register char rule_file_flag = 0;
  22.  
  23.     DBUG_ENTER("main");
  24.     /*
  25.      * Initialize global variables:
  26.      */
  27.     Beginact = 0;
  28.     Endact = 0;
  29.     Rules = 0;
  30.     Rulep = 0;
  31.     Filename = 0;
  32.     Linecount = 0;
  33.     Saw_break = 0;
  34.     Stackptr = Stackbtm - 1;
  35.     Stacktop = Stackbtm + MAXSTACKSZ;
  36.     Nextvar = Vartab;
  37.     init_pop_array();
  38.  
  39.     strcpy( Fieldsep, " \t" );
  40.     strcpy( Recordsep, "\n" );
  41.  
  42.     /*
  43.      * Parse command line
  44.      */
  45.     while ( --argc )
  46.     {
  47.         if ( **(++argv) == '-' )
  48.         {
  49.             /*
  50.              * Process dash options.
  51.              */
  52.             switch ( tolower( argv[0][1] ) )
  53.             {
  54.             case '#':
  55.                 DBUG_PUSH(&argv[0][2]);
  56.                 continue;
  57.             case 'f':
  58.                 if(!gotrules) {
  59.                     rule_file_flag++;
  60.                     argv++;
  61.                     argc--;
  62.                 } else
  63.                     usage();
  64.                 break;
  65.             case 0:
  66.                 if(!gotrules)
  67.                     rule_file_flag++;
  68.                 getstdin++;
  69.                 break;
  70.             default: usage();
  71.             }
  72.         }
  73.         if ( gotrules )
  74.         {
  75.             /*
  76.              * Already read rules file - assume this is
  77.              * is a text file for processing.
  78.              */
  79.             if ( ++didfile == 1 && Beginact )
  80.                 doaction( Beginact );
  81.             if ( getstdin )
  82.             {
  83.                 getstdin--;
  84.                 newfile( 0 );
  85.             }
  86.             else
  87.                 newfile( *argv );
  88.             process();
  89.         }
  90.         else
  91.         {
  92.             if(rule_file_flag) {
  93.                 if ( getstdin )
  94.                 {
  95.                     getstdin--;
  96.                     newfile( 0 );
  97.                 }
  98.                 else
  99.                     newfile( *argv );
  100.             } else
  101.                 pattern_arg = *argv;
  102.             compile();
  103.             pattern_arg = NULL;
  104.             gotrules = 1;
  105.         }
  106.     }
  107.     if ( !gotrules )
  108.         usage();
  109.  
  110.     if ( ! didfile )
  111.     {
  112.         /*
  113.          * Didn't process any files yet - process stdin.
  114.          */
  115.         newfile( 0 );
  116.         if ( Beginact )
  117.             doaction( Beginact );
  118.         process();
  119.     }
  120.     if ( Endact )
  121.         doaction( Endact );
  122.     DBUG_RETURN(0);
  123. }
  124.  
  125. /*
  126.  * Regular expression/action file compilation routines.
  127.  */
  128. void compile()
  129. {
  130.     /*
  131.      * Compile regular expressions and C actions into Rules struct,
  132.      * reading from current input file "Fileptr".
  133.      */
  134.     register int c;
  135.     register EXPR_NODE *root;
  136.  
  137.     DBUG_ENTER("compile");
  138.  
  139.     while ( (c = getcharacter()) != -1 )
  140.     {
  141.         if ( c==' ' || c=='\t' || c=='\n' )
  142.             /* swallow whitespace */
  143.             ;
  144.         else if ( c=='#' )
  145.         {
  146.             /*
  147.              * Swallow comments
  148.              */
  149.             while ( (c=getcharacter()) != -1 && c!='\n' )
  150.                 ;
  151.         }
  152.         else if ( c=='{' )
  153.         {
  154.             DBUG_PRINT("compile",("action"));
  155.             /*
  156.              * Compile the action string into a parse tree
  157.              */
  158.             ungetcharacter( (char) '{' );
  159.  
  160.             if ( Rulep && Rulep->action )
  161.             {
  162.                 Rulep->nextrule = (RULE *) 
  163.                     get_clear_memory( sizeof( *Rulep ) );
  164.                 Rulep = Rulep->nextrule;
  165.             }
  166.             if ( !Rulep )
  167.             {
  168.                 /*
  169.                  * This is the first action encountered.
  170.                  * Allocate the first Rules structure and
  171.                  * initialize it
  172.                  */
  173.                 Rules = Rulep = (RULE *)
  174.                     get_clear_memory( sizeof( *Rulep ) );
  175.             }
  176.             Rulep->action = act_compile( Workbuf );
  177.         }
  178.         else if ( c==',' )
  179.         {
  180.             DBUG_PRINT("compile",("stop pattern"));
  181.             /*
  182.              * It's (hopefully) the second part of a two-part
  183.              * pattern string.  Swallow the comma and start
  184.              * compiling an action string.
  185.              */
  186.             if ( !Rulep || !Rulep->pattern.start )
  187.                 error( "stop pattern without a start",
  188.                     RE_ERROR );
  189.             if ( Rulep->pattern.stop )
  190.                 error( "already have a stop pattern",
  191.                     RE_ERROR );
  192.             Rulep->pattern.stop = pat_compile( Workbuf );
  193.         }
  194.         else
  195.         {
  196.             /*
  197.              * Assume it's a regular expression pattern
  198.              */
  199.             DBUG_PRINT("compile",("start pattern"));
  200.  
  201.             ungetcharacter( (char) c );
  202.             root = pat_compile( Workbuf );
  203.  
  204.             if ( *Workbuf == T_BEGIN )
  205.             {
  206.                 /*
  207.                  * Saw a "BEGIN" keyword - compile following
  208.                  * action into special "Beginact" parse tree.
  209.                  */
  210.                 Beginact = act_compile( Workbuf );
  211.                 continue;
  212.             }
  213.             if ( *Workbuf == T_END )
  214.             {
  215.                 /*
  216.                  * Saw an "END" keyword - compile following
  217.                  * action into special "Endact" parse tree.
  218.                  */
  219.                 Endact = act_compile( Workbuf );
  220.                 continue;
  221.             }
  222.             if ( Rulep )
  223.             {
  224.                 /*
  225.                  * Already saw a pattern/action - link in
  226.                  * another Rules structure.
  227.                  */
  228.                 Rulep->nextrule = (RULE *)
  229.                     get_clear_memory( sizeof( *Rulep ) );
  230.                 Rulep = Rulep->nextrule;
  231.             }
  232.             if ( !Rulep )
  233.             {
  234.                 /*
  235.                  * This is the first pattern encountered.
  236.                  * Allocate the first Rules structure and
  237.                  * initialize it
  238.                  */
  239.                 Rules = Rulep = (RULE *) 
  240.                     get_clear_memory( sizeof( *Rulep ) );
  241.             }
  242.             if ( Rulep->pattern.start )
  243.                 error( "already have a start pattern",
  244.                     RE_ERROR );
  245.  
  246.             Rulep->pattern.start = root;
  247.         }
  248.     }
  249.     for(Rulep = Rules; Rulep; Rulep = Rulep->nextrule)
  250.     {
  251.         if(!Rulep->action) {
  252.             pattern_arg = "{printf \"%s\n\", $0}";
  253.             Rulep->action = act_compile( Workbuf );
  254.             pattern_arg = NULL;
  255.         }
  256.     }
  257.     endfile();
  258.     DBUG_VOID_RETURN;
  259. }
  260.  
  261. /*
  262.  * Text file main processing loop.
  263.  */
  264. void process()
  265. {
  266.     /*
  267.      * Read a line at a time from current input file at "Fileptr",
  268.      * then apply each rule in the Rules chain to the input line.
  269.      */
  270.     register int i;
  271.  
  272.     DBUG_ENTER("process");
  273.  
  274.     Recordcount = 0;
  275.  
  276.     while ( getline() )
  277.     {
  278.         /*
  279.          * Parse the input line.
  280.          */
  281.         if(! *Recordsep )
  282.             strcpy(Fieldsep," \t\n");
  283.         Fieldcount = parse( Linebuf, Fields, Fieldsep );
  284.         DBUG_PRINT("process",( "parsed %d words:", Fieldcount ));
  285.         DBUG_EXECUTE("process",for(i=0; i<Fieldcount; ++i )DBUG_PRINT("process",("<%s>",Fields[i])););
  286.  
  287.         Rulep = Rules;
  288.         while(Rulep)
  289.         {
  290.             if ( ! Rulep->pattern.start )
  291.             {
  292.                 /*
  293.                  * No pattern given - perform action on
  294.                  * every input line.
  295.                  */
  296.                 doaction( Rulep->action );
  297.             }
  298.             else if ( Rulep->pattern.startseen )
  299.             {
  300.                 /*
  301.                  * Start pattern already found - perform
  302.                  * action then check if line matches
  303.                  * stop pattern.
  304.                  */
  305.                 doaction( Rulep->action );
  306.                 if ( dopattern( Rulep->pattern.stop ) )
  307.                     Rulep->pattern.startseen = 0;
  308.             }
  309.             else if ( dopattern( Rulep->pattern.start ) )
  310.             {
  311.                 /*
  312.                  * Matched start pattern - perform action.
  313.                  * If a stop pattern was given, set "start
  314.                  * pattern seen" flag and process every input
  315.                  * line until stop pattern found.
  316.                  */
  317.                 doaction( Rulep->action );
  318.                 if ( Rulep->pattern.stop )
  319.                     Rulep->pattern.startseen = 1;
  320.             }
  321.             Rulep = Rulep->nextrule;
  322.         }
  323.     }
  324.     DBUG_VOID_RETURN;
  325. }
  326.  
  327. /*
  328.  * Miscellaneous functions
  329.  */
  330. parse( str, wrdlst, delim )
  331. register char *str;
  332. char *wrdlst[];
  333. char *delim;
  334. {
  335.     /*
  336.      * Parse the string of words in "str" into the word list at "wrdlst".
  337.      * A "word" is a sequence of characters delimited by one or more
  338.      * of the characters found in the string "delim".
  339.      * Returns the number of words parsed.
  340.      */
  341.     register int wrdcnt;
  342.     register char *cp, *wrdcp, c;
  343.     char wrdbuf[ MAXLINELEN+1 ];
  344.  
  345.     DBUG_ENTER("parse");
  346.     wrdcnt = 0;
  347.     while ( *str )
  348.     {
  349.         while(c = *str++)
  350.         {
  351.             cp = delim;
  352.             while(*cp && c != *cp)
  353.                 cp++;
  354.             if(! *cp)
  355.                 break;
  356.         }
  357.         str--;
  358.         if ( !*str )
  359.             break;
  360.         wrdcp = wrdbuf;
  361.         while(c = *str++)
  362.         {
  363.             cp = delim;
  364.             while(*cp && c != *cp)
  365.                 cp++;
  366.             if(*cp)
  367.                 break;            
  368.             *wrdcp++ = c;
  369.         }
  370.         str--;
  371.         *wrdcp = 0;
  372.         /*
  373.          * NOTE: allocate a MAXLINELEN sized buffer for every
  374.          * word, just in case user wants to copy a larger string
  375.          * into a field.
  376.          */
  377.         if(wrdcnt == max_field_count)
  378.         {
  379.             wrdlst[ wrdcnt ] = getmemory( MAXLINELEN+1 );
  380.             max_field_count++;
  381.         }
  382.         strcpy( wrdlst[ wrdcnt++ ], wrdbuf );
  383.     }
  384.     DBUG_RETURN(wrdcnt);
  385. }
  386.  
  387. void unparse( wrdlst, wrdcnt, str, delim )
  388. char *wrdlst[];
  389. register int wrdcnt;
  390. register char *str;
  391. char *delim;
  392. {
  393.     /*
  394.      * Replace all the words in "str" with the words in "wrdlst",
  395.      * maintaining the same word seperation distance as found in
  396.      * the string.
  397.      * A "word" is a sequence of characters delimited by one or more
  398.      * of the characters found in the string "delim".
  399.      */
  400.     register int wc;
  401.     register char *sp, *cp, c;
  402.     char strbuf[ MAXLINELEN+1 ], *start;
  403.  
  404.     DBUG_ENTER("unparse");
  405.     wc = 0;        /* next word in "wrdlst" */
  406.     sp = strbuf;    /* points to our local string */
  407.     start = str;    /* save start address of "str" for later... */
  408.     while ( *str )
  409.     {
  410.         /*
  411.          * Copy the field delimiters from the original string to
  412.          * our local version.
  413.          */
  414.         while(c = *str++)
  415.         {
  416.             cp = delim;
  417.             while(*cp && c != *cp)
  418.                 cp++;
  419.             if(!*cp)
  420.                 break;
  421.             *sp++ = c;
  422.         }
  423.         str--;
  424.         if ( !*str )
  425.             break;
  426.         /*
  427.          * Skip over the field in the original string and...
  428.          */
  429.         while(c = *str++)
  430.         {
  431.             cp = delim;
  432.             while(*cp && c != *cp)
  433.                 cp++;
  434.             if(*cp)
  435.                 break;
  436.         }
  437.         str--;
  438.         if ( wc < wrdcnt )
  439.         {
  440.             /*
  441.              * ...copy in the field in the wordlist instead.
  442.              */
  443.             cp = wrdlst[ wc++ ];
  444.             while(*sp++ = *cp++);
  445.             sp--;
  446.         }
  447.     }
  448.     /*
  449.      * Tie off the local string, then copy it back to caller's string.
  450.      */
  451.     *sp = 0;
  452.     strcpy( start, strbuf );
  453.     DBUG_VOID_RETURN;
  454. }
  455.  
  456. char *
  457. getmemory( len )
  458. register unsigned len;
  459. {
  460.     register char *cp;
  461.  
  462.     DBUG_ENTER("getmemory");
  463.     if ( cp=malloc( len ) )
  464.         DBUG_RETURN(cp);
  465.     error( "out of memory", MEM_ERROR );
  466.     DBUG_RETURN(NULL);
  467. }
  468.  
  469. char *
  470. get_clear_memory( len )
  471. register unsigned len;
  472. {
  473.     register char *cp;
  474.  
  475.     DBUG_ENTER("getmemory");
  476.     if ( cp=calloc( 1, len ) )
  477.         DBUG_RETURN(cp);
  478.     error( "out of memory", MEM_ERROR );
  479.     DBUG_RETURN(NULL);
  480. }
  481.  
  482. EXPR_NODE *get_expr_node(operator)
  483. char operator;
  484. {
  485.     register EXPR_NODE *node;
  486.  
  487.     DBUG_ENTER("get_expr_node");
  488.     node = (EXPR_NODE *) getmemory(sizeof(EXPR_NODE));
  489.     node->left = node->right = NULL;
  490.     node->operator = operator;
  491.     DBUG_PRINT("get_expr_node",("operator = '%s'",token_name[operator]));
  492.     DBUG_RETURN(node);
  493. }
  494.  
  495. void newfile( s )
  496. register char *s;
  497. {
  498.     DBUG_ENTER("newfile");
  499.     Linecount = 0;
  500.     if ( Filename = s )
  501.     {
  502. #ifdef BDS_C
  503.         if ( fopen( s, Fileptr = Curfbuf ) == -1 )
  504. #else
  505.         if ( !(Fileptr = fopen( s, "r" )) )
  506. #endif
  507.             error( "file not found", FILE_ERROR );
  508.     }
  509.     else
  510.     {
  511.         /*
  512.          * No file name given - process standard input.
  513.          */
  514.         Fileptr = stdin;
  515.         Filename = "standard input";
  516.     }
  517.     DBUG_VOID_RETURN;
  518. }
  519.  
  520. getline()
  521. {
  522.     /*
  523.      * Read a record from current input file.
  524.      */
  525.     register int rtn, len = 0;
  526.     register char *cp = Linebuf, *last_nl, *sep = Recordsep;
  527.  
  528.     DBUG_ENTER("getline");
  529.     if(eof_seen)
  530.     {
  531.         endfile();
  532.         DBUG_RETURN(0);
  533.     }
  534.     if(*sep)
  535.     {
  536.         while((*cp++ = rtn = getcharacter()) != *sep++ && rtn != -1)
  537.         {
  538.             while(*sep)
  539.             {
  540.                 if(rtn == *sep++)
  541.                     break;
  542.             }
  543.             if( ++len == MAXLINELEN )
  544.                 error("Input record too long", RECORD_ERROR);
  545.             sep = Recordsep;
  546.         }
  547.     } else    /* Treat an empty line as record separator. */
  548.     {
  549.         while(1)
  550.         {
  551.             last_nl = cp;
  552.             while((*cp++ = rtn = getcharacter()) != '\n' &&
  553.                   rtn != -1)
  554.             {
  555.                 if( ++len == MAXLINELEN )
  556.                     error("Input record too long",
  557.                           RECORD_ERROR);
  558.             }
  559.             if(((cp - last_nl) == 1) || (rtn == -1))
  560.                 break;
  561.         }
  562.     }
  563.     *(--cp) = 0;
  564.     if ( rtn == -1 )
  565.     {
  566.         if(len)
  567.             eof_seen = 1;
  568.         else
  569.         {
  570.             endfile();
  571.             DBUG_RETURN(0);
  572.         }
  573.     }
  574.     ++Recordcount;
  575.     DBUG_RETURN(1);
  576. }
  577.  
  578. int getcharacter()
  579. {
  580.     /*
  581.      * Read a character from curren input file.
  582.      * WARNING: your getc() must convert lines that end with CR+LF
  583.      * to LF and CP/M's EOF character (^Z) to a -1.
  584.      * Also, getc() must return a -1 when attempting to read from
  585.      * an unopened file.
  586.      */
  587.     register int c;
  588.  
  589.     DBUG_ENTER("getcharacter");
  590.     if(pattern_arg) {
  591.         if(ungetc_arg) {
  592.             c = ungetc_arg;
  593.             ungetc_arg = 0;
  594.         } else if(*pattern_arg)
  595.             c = *pattern_arg++;
  596.         else
  597.             c = EOF;
  598.     } else {
  599. #ifdef BDS_C
  600.         /*
  601.          * BDS C doesn't do CR+LF to LF and ^Z to -1 conversions
  602.          * <gag>
  603.          */
  604.         if ( (c = getc( Fileptr )) == '\r' )
  605.         {
  606.             if ( (c = getc( Fileptr )) != '\n' )
  607.             {
  608.                 ungetc( c );
  609.                 c = '\r';
  610.             }
  611.         }
  612.         else if ( c == 26 )    /* ^Z */
  613.             c = -1;
  614. #else
  615.         c = getc( Fileptr );
  616. #endif
  617.  
  618.         if ( c=='\n' )
  619.             ++Linecount;
  620.     }
  621.     DBUG_PRINT("getcharacter",("'%c'", c));
  622.     DBUG_RETURN(c);
  623. }
  624.  
  625. ungetcharacter( c )
  626. register char c;
  627. {
  628.     /*
  629.      * Push a character back into the input stream.
  630.      * If the character is a record seperator, or a newline character,
  631.      * the record and line counters are adjusted appropriately.
  632.      */
  633.     DBUG_ENTER("ungetcharacter");
  634.     if ( c == *Recordsep )
  635.         --Recordcount;
  636.     if ( c=='\n' )
  637.         --Linecount;
  638.     DBUG_PRINT("ungetcharacter",("'%c'", c));
  639.     if(pattern_arg)
  640.         DBUG_RETURN(ungetc_arg = c);
  641.     DBUG_RETURN(ungetc( c, Fileptr ));
  642. }
  643.  
  644. void endfile()
  645. {
  646.     DBUG_ENTER("endfile");
  647.     fclose( Fileptr );
  648.     eof_seen = 0;
  649.     Filename = NULL;
  650.     Linecount = 0;
  651.     DBUG_VOID_RETURN;
  652. }
  653.  
  654. void error( s, severe )
  655. register char *s;
  656. register int severe;
  657. {
  658.     DBUG_ENTER("error");
  659.     if ( Filename )
  660.         fprintf( stderr, "%s:", Filename );
  661.  
  662.     if ( Linecount )
  663.         fprintf( stderr, " line %d:", Linecount );
  664.  
  665.     fprintf( stderr, " %s\n", s );
  666.     if ( severe )
  667.         exit( severe );
  668.     DBUG_VOID_RETURN;
  669. }
  670.  
  671. void usage()
  672. {
  673.     DBUG_ENTER("usage");
  674.     error( "Usage: bawk { action | - | -f <actfile> } <file> ...",
  675.            USAGE_ERROR );
  676.     DBUG_VOID_RETURN;
  677. }
  678.